#include <QtGui>
#include <math.h>
#include <iomanip>
#include <fstream>
#include "mainWindow.h"
#include "glwidget.h"
#include "guiConvergencePlot.h"
#include "guiTypedefs.h"
#include "guiProblem.h"
#include "../guiCode/guiSolverThread.h"

extern Problem *problem;




void mainWindow::postProcessTabSelection (QWidget * w)
{
   int i;
   i = postProcessTab->currentIndex();
   
   if (i == 0) {         // Contour Page
      visualizeContourButton->setChecked(true);
      problem->setvisualizeState(SELECT);
   } else if (i == 1) {  // Streamline Page
      visualizeStreamlineButton->setChecked(true);
      problem->setvisualizeState(ADDSTREAMLINE);
   } else if (i == 2) {  // Probe Page
      visualizeProbeButton->setChecked(true);
      problem->setvisualizeState(PROBE);
   }

}  // End of postProcessTabSelection




void mainWindow::openResultFile()
{
   int i, j, dummyNstreamlines;
   float **firstPointCoor;

   if (problem->getCfdFileName() == "") {  // Do not open any result file if the problem is not saved.
      appendMessage(tr("WARNING: First you need to save the problem you are working on or open a previously created problem."), Qt::black);
      return;
   }

   QString dir = QString(problem->getDir().c_str());

   QString fileName = QFileDialog::getOpenFileName(this, tr("Open Result File"), dir, tr("Problem Files (*.dat)"));

   if (!fileName.isEmpty()){
      QFileInfo file( fileName );
      string dir = file.absolutePath().toStdString();
      string baseName = file.completeBaseName().toStdString();
      problem->setResultFileName(dir + "/" + baseName + ".dat");   // e.g. C:\VFL\Problems\MyProblem_100.dat

      appendMessage(tr("Result file is opened."), Qt::black);

      readResultFile();
      if (retainStyleCheck->isChecked() ) {
         // Recreate all the existing streamlines. This can be done in a separate function

         // Copy the starting points of the streamlines
         firstPointCoor = new float*[problem->nStreamlines];
         for(i=0; i<problem->nStreamlines; i++) firstPointCoor[i] = new float[2];

         for(i=0; i<problem->nStreamlines; i++) {
            firstPointCoor[i][0] = problem->streamlines[i].coor[0][0];
            firstPointCoor[i][1] = problem->streamlines[i].coor[0][1];
         }

         dummyNstreamlines = problem->nStreamlines;
         clearAllStreamlinesButtonClicked();

         // Redraw the streamlines
         for(i=0; i<dummyNstreamlines; i++) {
            addStreamline(firstPointCoor[i][0], firstPointCoor[i][1]);
         }

         for(j=0; j<problem->nStreamlines; j++) delete[] firstPointCoor[j];
         delete[] firstPointCoor;

      } else {
         boundaryCheckBox->setChecked(TRUE);
         meshCheckBox->setChecked(FALSE);
         contourCheckBox->setChecked(TRUE);
         streamlineCheckBox->setChecked(FALSE);
         problem->showBoundary = TRUE;
         problem->showMesh = FALSE;
         problem->showContour = TRUE;
         problem->showStreamline = FALSE;
         clearAllStreamlinesButtonClicked();
      }
   }
   glWidget->update();

}  // End of function openResultFile()




void mainWindow::readResultFile()
{
   // Cuneyt: No multi-block support.

   // This creates the .DAT file generated by the solver.
   // .DAT file is written in Tecplot format. It has the following variables
   // x, y, u, v, p, u_res, v_res, p_res, vorticity

   int i, j, nX, nY;
   double d1, d2;
   ifstream outFile;

   nX = problem->mesh->blocks[0].getnXpoints();
   nY = problem->mesh->blocks[0].getnYpoints();

   // Deallocate and allocate necessary matrices
   if (problem->uResult != NULL) {
      for(i=0; i<nX; i++) delete[] problem->uResult[i];
      delete[] problem->uResult;
      problem->uResult = NULL;

      for(i=0; i<nX; i++) delete[] problem->vResult[i];
      delete[] problem->vResult;
      problem->vResult = NULL;

      for(i=0; i<nX; i++) delete[] problem->pResult[i];
      delete[] problem->pResult;
      problem->pResult = NULL;
   }
   
   problem->uResult = new double*[nX];
   for(i=0; i<nX; i++) problem->uResult[i] = new double[nY];

   problem->vResult = new double*[nX];
   for(i=0; i<nX; i++) problem->vResult[i] = new double[nY];

   problem->pResult = new double*[nX];
   for(i=0; i<nX; i++) problem->pResult[i] = new double[nY];

   // Start reading the .DAT file
   outFile.open(problem->getResultFileName().c_str(), ios::in);

   // Read and ignore the file header
   outFile.ignore(256, '\n');
   outFile.ignore(256, '\n');
   outFile.ignore(256, '\n');

   for (j = 0; j < nY; j++) {
      for (i = 0; i < nX; i++) {
         outFile >> d1 >> d2 >> problem->uResult[i][j] >> problem->vResult[i][j] >> problem->pResult[i][j];
         outFile.ignore(256, '\n');
      }
   }

}  // End of function readResultFile()




void mainWindow::showContoursButtonClicked()
{
   contourCheckBox->setChecked(true);
   glWidget->update();
}




void mainWindow::visualizeChecksToggled()
{
   problem->showBoundary = boundaryCheckBox->isChecked();
   problem->showMesh = meshCheckBox->isChecked();
   problem->showContour = contourCheckBox->isChecked();
   problem->showStreamline = streamlineCheckBox->isChecked();
   glWidget->update();
}




void mainWindow::contourVarChanged(int i)
{
   problem->contourVar = i;
   glWidget->update();
}




void mainWindow::contourStyleChanged(int i)
{
   problem->contourStyle = i;
   glWidget->update();
}




void mainWindow::colorMapChanged(int i)
{
   problem->colorMap = i;
   glWidget->update();
}




void mainWindow::contourLevelChanged(const QString & str)
{
   problem->nContourLevels = str.toInt();
}




void mainWindow::visualizeButtonsClicked()
{
   // Shows the corresponding page of the postProcessTab when a button is pressed.
   // Sets the visualizeState variable.
   if (visualizeContourButton->isChecked()) {
      postProcessTab->setCurrentIndex(0);
      problem->setvisualizeState(SELECT);
   } else if (visualizeStreamlineButton->isChecked()) {
      postProcessTab->setCurrentIndex(1);
      problem->setvisualizeState(ADDSTREAMLINE);
   } else if (visualizeProbeButton->isChecked()) {
      postProcessTab->setCurrentIndex(2);
      problem->setvisualizeState(PROBE);
   //} else if (visualizeDataExtractButton->isChecked()) {
   //   // ...
   //   // problem->setvisualizeState(DATAEXTRACT);
   } else 	if (visualizeSelectButton->isChecked()) {
      problem->setvisualizeState(SELECT);
   } else {
      visualizeSelectButton->setChecked(TRUE);
      problem->setvisualizeState(SELECT);
   }

}  // End of function visualizeButtonsClicked()




void mainWindow::probeButtonClicked()
{
   float x, y;
   int b, c1, I, J, nX, nY;

   if (problem->getResultFileName() == "")  // Do nothing if a result file is not opened.
      return;

   if (probeX->text().isEmpty() || probeY->text().isEmpty() )
      return;

   if (probeXYradio->isChecked()) {  // Probe at given (x,y) coordinates
      x = probeX->text().toFloat();
      y = probeY->text().toFloat();
      probeAtPosition(x, y);
   } else {  // Probe at given (I,J) indices
      I = probeX->text().toInt();
      J = probeY->text().toInt();
      
      b = 0;  // Cuneyt: No multi-block support.
      nX = problem->mesh->blocks[b].getnXpoints();
      nY = problem->mesh->blocks[b].getnYpoints();

      if (I<1 || I>nX || J<1 || J>nY) {  // I, J indices are outside the range
         // Cuneyt: A warning message can be provided to remind nX and nY.
         probeResultX->setText("");
         probeResultY->setText("");
         probeResultU->setText("");
         probeResultV->setText("");
         probeResultP->setText("");
         return;
      } else {
         // Find the corresponding x and y
         c1 = (J-1) * nX + (I-1);
         x = problem->mesh->blocks[b].coordinates[2*c1];
         y = problem->mesh->blocks[b].coordinates[2*c1+1];
         probeAtPosition(x, y);
      }
   }
}  // End of function probeButtonClicked()





void mainWindow::probeAtPosition(float x, float y)
{
   int b, cell, i, iCell, jCell, nX, nY;
   double var[3][4];          // u, v, p values at the 4 corners of a cell.
   double interpolation[3];   // u, v, p interpolation at point (x,y).
   float **cornerCoor;        // Corner coordinates of a cell

   // First find in which cell the point (x,y) lies in
   b = 0;  // Cuneyt: No multi-block support
   nX = problem->mesh->blocks[b].getnXpoints();
   nY = problem->mesh->blocks[b].getnYpoints();

   cornerCoor = new float*[4];
   for(i=0; i<4; i++) cornerCoor[i] = new float[2];

   // Find in which cell the point (x,y) lies in
   cell = problem->mesh->blocks[b].findCellAtXY(x, y, iCell, jCell, cornerCoor);

   // Cuneyt: isCellFound can be set to FALSE if point (x,y) is inside ablcoked cell.

   if (cell > -1 && !problem->mesh->blocks[b].isCellBlocked[cell]) {	  // If a non-blocked cell is found
      // Now we can interpolate for the variables at (x,y). Perform bi-linear interpolation.

      // Get the variables at the 4 corners of this cell
      var[0][0] = problem->uResult[iCell][jCell];
      var[0][1] = problem->uResult[iCell+1][jCell];
      var[0][2] = problem->uResult[iCell+1][jCell+1];
      var[0][3] = problem->uResult[iCell][jCell+1];

      var[1][0] = problem->vResult[iCell][jCell];
      var[1][1] = problem->vResult[iCell+1][jCell];
      var[1][2] = problem->vResult[iCell+1][jCell+1];
      var[1][3] = problem->vResult[iCell][jCell+1];

      var[2][0] = problem->vResult[iCell][jCell];
      var[2][1] = problem->vResult[iCell+1][jCell];
      var[2][2] = problem->vResult[iCell+1][jCell+1];
      var[2][3] = problem->vResult[iCell][jCell+1];

      // Cuneyt: All the cells are assumed to be Cartesian.
      // First interpolate in the x direction
      for (i=0; i<3; i++) {
         // double fR1 = ( (cornerCoor[1][0]-x)*var[i][0] + (x-cornerCoor[0][0])*var[i][1] ) / (cornerCoor[1][0]-cornerCoor[0][0]);
         // double fR2 = ( (cornerCoor[1][0]-x)*var[i][3] + (x-cornerCoor[0][0])*var[i][2] ) / (cornerCoor[1][0]-cornerCoor[0][0]);

         // Now interpolate in the y direction
         //double fP = ( (cornerCoor[2][1]-y)*fR1 + (y-cornerCoor[0][1])*fR2 ) / (cornerCoor[2][1]-cornerCoor[0][1]);

         // Now calculate the interpolated value.
         interpolation[i] = (var[i][0]*(cornerCoor[1][0]-x)*(cornerCoor[2][1]-y) + var[i][1]*(x-cornerCoor[0][0])*(cornerCoor[2][1]-y) + 
                            var[i][3]*(cornerCoor[1][0]-x)*(y-cornerCoor[0][1]) + var[i][2]*(x-cornerCoor[0][0])*(y-cornerCoor[0][1])) /
                          ((cornerCoor[1][0]-cornerCoor[0][0])*(cornerCoor[2][1]-cornerCoor[0][1]));
      }

      // Now show the results of probing inside the proper text boxes.
      probeResultX->setText(QString("%1").arg(x));
      probeResultY->setText(QString("%1").arg(y));
      probeResultU->setText(QString("%1").arg(interpolation[0]));
      probeResultV->setText(QString("%1").arg(interpolation[1]));
      probeResultP->setText(QString("%1").arg(interpolation[2]));

   } else {
      // Probed point is not inside the problem domain.
      probeResultX->setText(QString("%1").arg(x));
      probeResultY->setText(QString("%1").arg(y));
      probeResultU->setText("");
      probeResultV->setText("");
      probeResultP->setText("");
   }

}  // End of function probeAtPosition()




void mainWindow::probeRadioButtonsToggled(bool flag)
{
   if (flag) {
      probeXlabel->setText("x");
      probeYlabel->setText("y");
   } else {
      probeXlabel->setText("I");
      probeYlabel->setText("J");
   }
}




void mainWindow::addStreamline(float x, float y)
{
   int b, i, k, nS, nX, nY;
   int cell, iCell, jCell, neighborCells[9], pointCounter;
   double dx, dy, theta, distance;
   double var[2][4];  // u, v values at the 4 corners of a cell.
   double speed[2];   // u, v interpolation at point (x,y).
   float **dummyCoor;
   float **cornerCoor;   // Corner coordinates of a cell

   nS = problem->nStreamlines;  // Shortcut
   if (nS == MAX_STREAMLINES) return;  // No more streamline can be added.

   double stepSize = streamlineStepSizeEdit->text().toDouble();   // Step size for integration (% of the cell size)
   int nMaxSteps = streamlineMaxStepsEdit->text().toInt();        // Upper limit of integration steps

   pointCounter = 0;
   b = 0;   // Cuneyt: No multi-block support
   nX = problem->mesh->blocks[b].getnXpoints();
   nY = problem->mesh->blocks[b].getnYpoints();
   
   dummyCoor = new float*[nMaxSteps];
   for(i=0; i<nMaxSteps; i++) dummyCoor[i] = new float[2];

   dummyCoor[0][0] = x;
   dummyCoor[0][1] = y;

   cornerCoor = new float*[4];
   for(i=0; i<4; i++) {
      cornerCoor[i] = new float[2];
   }

   for(i=0; i<9; i++) {
      neighborCells[i] = -1;
   }

   for (k=1; k<nMaxSteps; k++) {
      // First find in which cell the point (x,y) lies in
      cell = problem->mesh->blocks[b].isXYinNeighbors(x, y, iCell, jCell, cornerCoor, neighborCells);
      if (cell == -1)
         cell = problem->mesh->blocks[b].findCellAtXY(x, y, iCell, jCell, cornerCoor);
      
      if (cell == -1) {  // If no cell was found, it means the streamline reached to a point outside the problem domain. STOP.
         break;  // Exit the k loop
      } else if (problem->mesh->blocks[b].isCellBlocked[cell]) {	// If cell is a blocked cell STOP.
         break;  // Exit k loop
      }

      // Now we can interpolate for the variables at (x,y). Perform bi-linear interpolation.

      // Get the variables at the 4 corners of this cell
      var[0][0] = problem->uResult[iCell][jCell];
      var[0][1] = problem->uResult[iCell+1][jCell];
      var[0][2] = problem->uResult[iCell+1][jCell+1];
      var[0][3] = problem->uResult[iCell][jCell+1];

      var[1][0] = problem->vResult[iCell][jCell];
      var[1][1] = problem->vResult[iCell+1][jCell];
      var[1][2] = problem->vResult[iCell+1][jCell+1];
      var[1][3] = problem->vResult[iCell][jCell+1];

      // Cuneyt: All the cells are assumed to be Cartesian.
      // First interpolate in the x direction
      for (i=0; i<2; i++) {
         // double fR1 = ( (cornerCoor[1][0]-x)*var[i][0] + (x-cornerCoor[0][0])*var[i][1] ) / (cornerCoor[1][0]-cornerCoor[0][0]);
         // double fR2 = ( (cornerCoor[1][0]-x)*var[i][3] + (x-cornerCoor[0][0])*var[i][2] ) / (cornerCoor[1][0]-cornerCoor[0][0]);

         // Now interpolate in the y direction
         //double fP = ( (cornerCoor[2][1]-y)*fR1 + (y-cornerCoor[0][1])*fR2 ) / (cornerCoor[2][1]-cornerCoor[0][1]);

         // Now calculate the interpolated value.
         speed[i] = (var[i][0]*(cornerCoor[1][0]-x)*(cornerCoor[2][1]-y) + var[i][1]*(x-cornerCoor[0][0])*(cornerCoor[2][1]-y) + 
                   var[i][3]*(cornerCoor[1][0]-x)*(y-cornerCoor[0][1]) + var[i][2]*(x-cornerCoor[0][0])*(y-cornerCoor[0][1])) /
                  ((cornerCoor[1][0]-cornerCoor[0][0])*(cornerCoor[2][1]-cornerCoor[0][1]));
      }
      
      // if (speed[0] == 0.0 && speed[1] == 0.0) {
      //    break;  // Streamline reached to a wall. Stop.
      // }

      // Let's find the location of the next point of the streamline.
      // We know that next point is stepSize*cellSize away from point(x,y).
      // Cuneyt: Cells are assumed to be Cartesian.
      distance = stepSize * min(fabs(cornerCoor[1][0]-cornerCoor[0][0]), fabs(cornerCoor[2][1]-cornerCoor[0][1]));
      
      // Its exact location is found from the direction of the speed vector.

      theta = atan2(fabs(speed[1]), fabs(speed[0]));
      dx = distance / sqrt(1 + tan(theta)*tan(theta));
      dy = dx * tan(theta);
      if (speed[0] >= 0) x = x + dx;
      else x = x - dx;

      if (speed[1] >= 0) y = y + dy;
      else y = y - dy;

      pointCounter = pointCounter + 1;
      dummyCoor[pointCounter][0] = x;
      dummyCoor[pointCounter][1] = y;


      // Let's find the neighbors of the "cell". Most probably the next point of the streamline is located in one these neighbors.
      // Note that for convenience cell itself is considered to be one of the neighbors.
      // If a neighbor is -1, it means it is either outside the problem domain or it is a blocked cell.
      neighborCells[0] = cell;
      neighborCells[1] = cell + 1;       // Right neighbor
      neighborCells[2] = cell - 1;       // Left neighbor	
      neighborCells[3] = cell + nX;      // Top neighbor
      neighborCells[4] = cell - nX;      // Bottom neighbor
      neighborCells[5] = cell + nX + 1;  // Top-right neighbor
      neighborCells[6] = cell + nX - 1;  // Top-left neighbor
      neighborCells[7] = cell - nX + 1;  // Bottom-right neighbor
      neighborCells[8] = cell - nX - 1;  // Bottom-left neighbor

      // Correct the neighbors that should actually fall outside the problem domain
      if (cell < nX-1) {  // cell is located at the bottom boundary
         neighborCells[4] = -1;
         neighborCells[7] = -1;
         neighborCells[8] = -1;
      }
      if (cell > (nX-1)*(nY-1)-nX) {  // cell is located at the top boundary
         neighborCells[3] = -1;
         neighborCells[5] = -1;
         neighborCells[6] = -1;
      }
      if (cell % (nX-1) == 0) {       // cell is located at the left boundary
         neighborCells[2] = -1;
         neighborCells[6] = -1;
         neighborCells[8] = -1;
      }
      if ((cell+1) % (nX-1) == 0) {   // cell is located at the right boundary
         neighborCells[1] = -1;
         neighborCells[5] = -1;
         neighborCells[7] = -1;
      }
   }
   
   if (pointCounter > 0) {  // Add this streamline
      // Allocate space for the streamline coordinates
      problem->streamlines[nS].coor = new float*[pointCounter];
      for(i=0; i<pointCounter; i++) problem->streamlines[nS].coor[i] = new float[2];

      // Copy dummyCoor to streamline coordinates
      for(i=0; i<pointCounter; i++) {
         problem->streamlines[nS].coor[i][0] = dummyCoor[i][0];
         problem->streamlines[nS].coor[i][1] = dummyCoor[i][1];
      }
      problem->streamlines[nS].nPoints = pointCounter;

      problem->nStreamlines = problem->nStreamlines + 1;

      if (!streamlineCheckBox->isChecked()) emit streamlineCheckBox->click();
   }
   
   // Deallocate dummyCoor
   for(i=0; i<nMaxSteps; i++) delete[] dummyCoor[i];
   delete[] dummyCoor;

   for(i=0; i<4; i++) delete[] cornerCoor[i];
   delete[] cornerCoor;

}  // End of function addStreamline()




void mainWindow::placeStreamlineRadioButtonsToggled(bool flag)
{
   if (placeStreamlineXYradio->isChecked()) {
      streamlineXlabel->setText("x");
      streamlineYlabel->setText("y");
   } else {
      streamlineXlabel->setText("I");
      streamlineYlabel->setText("J");
   }
}




void mainWindow::clearAllStreamlinesButtonClicked()
{
   int i, j;

   // Delete streamline coordinates
   for(i=0; i<problem->nStreamlines; i++) {
      for(j=0; j<problem->streamlines[i].nPoints; j++) delete[] problem->streamlines[i].coor[j];
      delete[] problem->streamlines[i].coor;
   }

   problem->nStreamlines = 0;
   glWidget->update();
}



void mainWindow::clearTheLastStreamlineButtonClicked()
{
   int i, j;

   if (problem->nStreamlines == 0) return;  // There is no streamline to erase.

   // Delete the streamline coordinates
   i = problem->nStreamlines - 1;
   for(j=0; j<problem->streamlines[i].nPoints; j++) delete[] problem->streamlines[i].coor[j];
   delete[] problem->streamlines[i].coor;

   problem->nStreamlines = problem->nStreamlines - 1;
   glWidget->update();
}



void mainWindow::placeStreamlineButtonClicked()
{
   float x, y;
   int b, c1, I, J, nX, nY;

   if (problem->getResultFileName() == "")  // Do nothing if a result file is not opened.
      return;

   if (placeStreamlineXedit->text().isEmpty() || placeStreamlineYedit->text().isEmpty() )
      return;

   if (placeStreamlineXYradio->isChecked()) {	// Place the streamline at given (x,y) coordinates
      x = placeStreamlineXedit->text().toFloat();
      y = placeStreamlineYedit->text().toFloat();
      addStreamline(x, y);
   } else {  // Place the streamline at given (I,J) indices
      I = placeStreamlineXedit->text().toInt();
      J = placeStreamlineYedit->text().toInt();
      
      b = 0;														// cuneyt: Multi-block destegi yok.
      nX = problem->mesh->blocks[b].getnXpoints();
      nY = problem->mesh->blocks[b].getnYpoints();

      if (I<1 || I>nX || J<1 || J>nY) {  // I, J indices are outside the range
         // cuneyt: nX ve nY'yi hatirlatan bir uyari vermek gerek.
         return;
      } else {
         // Find the corresponding x and y
         c1 = (J-1) * nX + (I-1);
         x = problem->mesh->blocks[b].coordinates[2*c1];
         y = problem->mesh->blocks[b].coordinates[2*c1+1];
         addStreamline(x, y);
      }
   }
   streamlineCheckBox->setChecked(true);
   glWidget->update();

}  // End of function placeStreamlineButtonClicked()
